﻿#region COPYRIGHT© 2005-2013 Phillip Clark. All rights reserved.

// For licensing information see License.txt (MIT style licensing).

#endregion

using System.Linq;

namespace FlitBit.Parse
{
	partial class CharRule
	{
		
		/// <summary>
		///   Rule matching the carriage return character.
		/// </summary>
		public static readonly Match CarriageReturn = new CharRuleNewline(CharMatch.Exact('\r'));

		/// <summary>
		///   Rule matching the carriage return, line feed combination.
		/// </summary>
		public static readonly Match CarriageReturnLineFeed = new CharRuleNewline(CharMatch.Exact('\r'), CharMatch.Exact('\n'));

		/// <summary>
		/// Decimal digit characters.
		/// </summary>
		public static readonly char[] DecimalDigitCharacters = "1234567890".ToCharArray();

		/// <summary>
		///   Matches a single decimal digit.
		/// </summary>
		public static readonly Match DecimalDigit = MatchAny(DecimalDigitCharacters);

		
		/// <summary>
		///   Matches decimal digits until a non-digit character or end is encountered.
		/// </summary>
		public static readonly Match DecimalDigits = MatchRepeatedly(MatchAny(DecimalDigitCharacters));

		/// <summary>
		/// Default match rule for a double-quote delimited string.
		/// </summary>
		public static readonly Match DoubleQuotedString = MatchAny(
																															 MatchExact(MatchExact('"')
																																					,
																																					MatchRepeatedly(MatchAny(MatchNone('\\', '"'), MatchExact('\\', '"'),
																																																	MatchExact('"', '"'), MatchExact('\\')))
																																					, MatchExact('"')),
																															MatchExact('"', '"')
			);

		
		/// <summary>
		/// Valid identifier characters in english frequency order.
		/// </summary>
		public static readonly char[] IdentifierCharacters =
			"etaionsrhlcdumfpgybwvkxqjzETAIONSRHLCDUMFPGYBWVKXQJZ_".ToCharArray();

		/// <summary>
		/// Default match rule for the first identifier character (same as C#).
		/// </summary>
		public static readonly Match FirstIdentifierCharacter = MatchAny(IdentifierCharacters);

		/// <summary>
		/// Default match rule for identifiers (same as C#).
		/// </summary>
		public static readonly Match Identifier =
			MatchAny(MatchExact(FirstIdentifierCharacter, MatchRepeatedly(MatchAny(FirstIdentifierCharacter, DecimalDigits))),
							FirstIdentifierCharacter);

		/// <summary>
		/// Hexidecimal digit characters.
		/// </summary>
		public static readonly char[] HexidecimalDigitCharacters = "1234567890AaBbCcDdEeFf".ToCharArray();

		/// <summary>
		///   Matches a single hexdecimal digit.
		/// </summary>
		public static readonly Match HexidecimalDigit = MatchAny(HexidecimalDigitCharacters);

		
		/// <summary>
		///   Matches hexidecimal digits until a non-digit character or end is encountered.
		/// </summary>
		public static readonly Match HexidecimalDigits = MatchRepeatedly(MatchAny(HexidecimalDigitCharacters));

		/// <summary>
		/// Match rule for month of year.
		/// </summary>
		public static readonly Match Iso8601Month = MatchAny(
																												 MatchExact(MatchExact('0'), MatchAny("123456789")),
																												MatchExact(MatchExact('1'), MatchAny("012")));

		/// <summary>
		/// Match rule for fractional seconds.
		/// </summary>
		public static readonly Match Iso8601TimeFractionalSeconds = MatchExact(MatchExact('.'),
																																					MatchRepeatedly(DecimalDigit, 7));

		/// <summary>
		/// Match rule for hours of day.
		/// </summary>
		public static readonly Match Iso8601TimeHours = MatchAny(
																														 MatchExact(MatchAny("01"), DecimalDigit),
																														MatchExact(MatchExact('2'), MatchAny("0123")));

		/// <summary>
		/// Match rule for minutes of hour.
		/// </summary>
		public static readonly Match Iso8601TimeMinutes = MatchExact(MatchAny("012345"), DecimalDigit);
		/// <summary>
		/// Match rule for seconds of minute.
		/// </summary>
		public static readonly Match Iso8601TimeSeconds = MatchExact(MatchAny("012345"), DecimalDigit);

		/// <summary>
		/// Match rule for seconds with fractional seconds.
		/// </summary>
		public static readonly Match Iso8601TimeSecondsWithFraction = MatchAny(
																																					 MatchExact(Iso8601TimeSeconds, Iso8601TimeFractionalSeconds),
																																					Iso8601TimeSeconds);
		/// <summary>
		/// Match rule for time of day.
		/// </summary>
		public static readonly Match Iso8601Time = MatchExact(
																												 Iso8601TimeHours, MatchExact(':'),
																												Iso8601TimeMinutes,
																												MatchExact(':'),
																												Iso8601TimeSeconds);

		/// <summary>
		/// Match rule for time with franctional seconds.
		/// </summary>
		public static readonly Match Iso8601TimeWithFractionalSeconds = MatchAny(
																																						 MatchExact(Iso8601Time, Iso8601TimeFractionalSeconds),
																																						Iso8601Time
			);

		/// <summary>
		/// Match rule for obscure times.
		/// </summary>
		public static readonly Match Iso8601TimeWithObscureTimes = MatchAny(
																																			 Iso8601TimeWithFractionalSeconds,
																																			MatchExact("24:00:00") /* evening midnight */,
																																			MatchExact("23:59:60") /* leap second */
			);

		/// <summary>
		/// Match rule for numeric timezones.
		/// </summary>
		public static readonly Match Iso8601Timezone = MatchAny(
																													 MatchExact('Z'),
																													MatchExact(
																																		 MatchAny('+', '-'),
																																		MatchAny(
																																						 MatchExact(MatchAny(
																																																 MatchExact(MatchAny("01"), DecimalDigit),
																																																MatchExact(MatchExact('2'), MatchAny("0123"))), MatchExact(':'),
																																												Iso8601TimeMinutes),
																																						MatchExact("24:00"),
																																						Iso8601TimeHours)));

		/// <summary>
		/// Match rule for ISO 8601 year.
		/// </summary>
		public static readonly Match Iso8601Year = MatchExact(DecimalDigit, DecimalDigit, DecimalDigit, DecimalDigit);

		/// <summary>
		/// Match rule for ISO 8601 expanded year.
		/// </summary>
		public static readonly Match Iso8601YearExpanded = MatchExact(MatchAny('+', '-'), DecimalDigits);

		/// <summary>
		/// Match rule for day of month.
		/// </summary>
		public static readonly Match Iso8601Day = MatchAny(
																											 MatchExact(MatchExact('0'), MatchAny("123456789")),
																											MatchExact(MatchAny('1', '2'), DecimalDigit),
																											MatchExact(MatchExact('3'), MatchAny("01")));

		/// <summary>
		/// Match rule for ISO-8601 date strings.
		/// </summary>
		public static readonly Match Iso8601Date = MatchExact(Iso8601Year,
																													MatchExact('-'),
																													Iso8601Month,
																													MatchExact('-'),
																													Iso8601Day
			);

		/// <summary>
		/// Match rule for ISO 8601 date time strings.
		/// </summary>
		public static readonly Match Iso8601DateTime = MatchExact(Iso8601Date, MatchExact('T'), Iso8601TimeWithObscureTimes,
																															Iso8601Timezone);

		/// <summary>
		///   Rule matching the line feed character.
		/// </summary>
		public static readonly Match LineFeed = new CharRuleNewline(CharMatch.Exact('\n'));

		/// <summary>
		///   Rule matching the line separator character.
		/// </summary>
		public static readonly Match LineSeparator = new CharRuleNewline(CharMatch.Exact('\u2028'));

		/// <summary>
		/// Match rule for lower-case identifiers.
		/// </summary>
		public static readonly char[] LowerCaseIdentifierCharacters = "etaionsrhlcdumfpgybwvkxqjz".ToCharArray();

		/// <summary>
		/// Match rule for C style multi-line comments.
		/// </summary>
		public static readonly Match MultiLineComment = MatchExact(MatchExact('/', '*')
																															, MatchRepeatedly(MatchNone(1, MatchExact('*', '/')))
																															, MatchExact('*', '/'));

		/// <summary>
		///   Rule matching the next line character.
		/// </summary>
		public static readonly Match NextLine = new CharRuleNewline(CharMatch.Exact('\u0085'));

		/// <summary>
		///   Rule matching the paragraph separator character.
		/// </summary>
		public static readonly Match ParagraphSeparator = new CharRuleNewline(CharMatch.Exact('\u2029'));

		/// <summary>
		///   Matches any of the character(s) that signify a newline.
		/// </summary>
		public static readonly Match NewLine = MatchAny(CarriageReturnLineFeed, CarriageReturn, LineFeed, LineSeparator,
																										ParagraphSeparator, NextLine);

		
		/// <summary>
		/// Match rule for single line comments.
		/// </summary>
		public static readonly Match SingleLineComment = MatchAny(MatchExact(MatchExact("//"),
																																				MatchRepeatedly(MatchNone(1, CarriageReturn
																																																	, LineSeparator
																																																	, ParagraphSeparator
																																																	, NextLine))),
																															MatchExact("//"));

		/// <summary>
		/// Match rule for single quoted strings.
		/// </summary>
		public static readonly Match SingleQuotedString = MatchExact(MatchExact('\'')
																																,
																																MatchRepeatedly(MatchAny(MatchNone('\\', '\''), MatchExact('\\', '\''),
																																												MatchExact('\'', '\''), MatchExact('\\')))
																																, MatchExact('\''));

		/// <summary>
		/// Match rule for C# formatted timespans.
		/// </summary>
		public static readonly Match Timespan = MatchExact(
																											 DecimalDigits,
																											MatchExact('.'),
																											Iso8601TimeWithFractionalSeconds
			);

		/// <summary>
		/// Match rule for upper-case identifiers.
		/// </summary>
		public static readonly char[] UpperCaseIdentifierCharacters = "ETAIONSRHLCDUMFPGYBWVKXQJZ".ToCharArray();

		/// <summary>
		/// Match rule for whitespace.
		/// </summary>
		public static readonly Match Whitespace = MatchRepeatedly(
																														 MatchAny(MatchAny(" \u0009\u0008\u000B\u000C".ToCharArray()), NewLine));

		/// <summary>
		/// Creates a match rule that matches all character matches provided.
		/// </summary>
		/// <param name="parts"></param>
		/// <returns></returns>
		public static CharRule MatchAll(params CharMatch[] parts)
		{
			return new CharRuleAll(parts);
		}

		/// <summary>
		/// Creates a match rule that matches any of the character matches provided.
		/// </summary>
		/// <param name="parts"></param>
		/// <returns></returns>
		public static CharRule MatchAny(params CharMatch[] parts)
		{
			return new CharRuleAny(parts);
		}

		/// <summary>
		/// Creates a match rule that matches any of the characters provided.
		/// </summary>
		/// <param name="characters"></param>
		/// <returns></returns>
		public static CharRule MatchAny(params char[] characters)
		{
			return new CharRuleAny((from c in characters
															select CharMatch.Exact(c)).ToArray());
		}

		/// <summary>
		/// Creates a match rule that matches any of the characters provided.
		/// </summary>
		/// <param name="characters"></param>
		/// <returns></returns>
		public static CharRule MatchAny(string characters)
		{
			return new CharRuleAny((from c in characters
															select CharMatch.Exact(c)).ToArray());
		}

		/// <summary>
		/// Creates a match rule that matches any of the characters provided (case insensitive).
		/// </summary>
		/// <param name="characters"></param>
		/// <returns></returns>
		public static CharRule MatchAnyCaseInsensitive(params char[] characters)
		{
			return new CharRuleAny((from c in characters
															select CharMatch.CaseInsensitive(c)).ToArray());
		}

		/// <summary>
		/// Creates a match rule that matches exactly the parts given.
		/// </summary>
		/// <param name="parts"></param>
		/// <returns></returns>
		public static CharRule MatchExact(params CharMatch[] parts)
		{
			return new CharRuleExact(parts);
		}

		/// <summary>
		/// Creates a match rule that matches exactly the characters given.
		/// </summary>
		/// <param name="characters"></param>
		/// <returns></returns>
		public static CharRule MatchExact(string characters)
		{
			return new CharRuleExact((from c in characters
																select CharMatch.Exact(c)).ToArray());
		}
		/// <summary>
		/// Creates a match rule that matches exactly the characters given.
		/// </summary>
		/// <param name="characters"></param>
		/// <returns></returns>
		public static CharRule MatchExact(params char[] characters)
		{
			return new CharRuleExact((from c in characters
																select CharMatch.Exact(c)).ToArray());
		}
		/// <summary>
		/// Creates a match rule that matches exactly the characters given.	(case insensitive)
		/// </summary>
		/// <param name="characters"></param>
		/// <returns></returns>
		public static CharRule MatchExactCaseInsensitive(string characters)
		{
			return new CharRuleExact((from c in characters
																select CharMatch.CaseInsensitive(c)).ToArray());
		}
		/// <summary>
		/// Creates a match rule that matches exactly the characters given.	(case insensitive)
		/// </summary>
		/// <param name="characters"></param>
		/// <returns></returns>
		public static CharRule MatchExactCaseInsensitive(params char[] characters)
		{
			return new CharRuleExact((from c in characters
																select CharMatch.CaseInsensitive(c)).ToArray());
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="parts"></param>
		/// <returns></returns>
		public static CharRule MatchNone(params CharMatch[] parts)
		{
			return new CharRuleAll(parts);
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="characters"></param>
		/// <returns></returns>
		public static CharRule MatchNone(params char[] characters)
		{
			return new CharRuleNone((from c in characters
															select CharMatch.Exact(c)).ToArray());
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="characters"></param>
		/// <returns></returns>
		public static CharRule MatchNoneCaseInsensitive(params char[] characters)
		{
			return new CharRuleNone((from c in characters
															select CharMatch.CaseInsensitive(c)).ToArray());
		}

		/// <summary>
		///   Matches a C-style comment (single or multi line).
		/// </summary>
		public static readonly Match CStyleComment = MatchAny(SingleLineComment, MultiLineComment);

	}
}